package com.yxsk.relay.job.admin.data.aop;

import com.yxsk.relay.job.admin.core.schedule.RelayJobScheduleRegistrar;
import com.yxsk.relay.job.component.common.exception.RelayJobRuntimeException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Stream;

/**
 * @Author 11376
 * @CreaTime 2019/6/6 16:37
 * @Description 判断service是否有调度服务注册器属性，并已启用组件, find方法排除
 */
@Slf4j
@Aspect
@Component
public class DependOnScheduleServiceFilter {

    @Pointcut("execution(* com.yxsk.relay.job.admin.data.service.*Service.*(..)) && !execution(* com.yxsk.relay.job.admin.data.service.*Service.find*(..))")
    public void point() {
    }

    @Before("point()")
    public void handle(JoinPoint joinPoint) {
        Object target = joinPoint.getTarget();

        Boolean refuse = this.getCache(target);
        if (refuse != null) {
            if (refuse) {
                throw new RelayJobRuntimeException("未启用相应的调度服务, 操作失败");
            }
            return;
        }

        Class<?> targetClass = target.getClass();
        if (targetClass.getAnnotation(NotCheckScheduleService.class) != null) {
            this.putCache(target, false);
            return;
        }
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        if (method.getAnnotation(NotCheckScheduleService.class) != null) {
            this.putCache(target, false);
            return;
        }
        Field[] fields = targetClass.getDeclaredFields();
        if (fields != null) {
            Stream.of(fields).forEach(field -> {
                field.setAccessible(true);
                try {
                    if (RelayJobScheduleRegistrar.class.isAssignableFrom(field.getType()) && field.get(target) == null) {
                        // 放入缓存
                        this.putCache(target, true);
                        throw new RelayJobRuntimeException("未启用相应的调度服务, 操作失败");
                    }
                } catch (IllegalAccessException e) {
                    log.error("获取属性错误", e);
                }
            });
        }

        this.putCache(target, false);
    }

    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(false);

    private Map<Object, Boolean> serviceInfo = new HashMap<>(16);

    private void putCache(Object target, boolean refuse) {
        this.readWriteLock.writeLock().lock();
        try {
            this.serviceInfo.put(target, refuse);
        } finally {
            this.readWriteLock.writeLock().unlock();
        }
    }

    private Boolean getCache(Object target) {
        this.readWriteLock.readLock().lock();
        try {
            return this.serviceInfo.get(target);
        } finally {
            this.readWriteLock.readLock().unlock();
        }
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Documented
    public @interface NotCheckScheduleService {

    }

}
